package com.demo.rpc.server;

import com.demo.rpc.common.RpcRequest;
import com.demo.rpc.common.RpcResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Map;

public class RpcServerHandler extends SimpleChannelInboundHandler<RpcRequest> {
    private static final Logger LOGGER = LoggerFactory.getLogger(RpcServerHandler.class);

    private final Map<String, Object> handlerMap;

    public RpcServerHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception {
        LOGGER.info("进入服务器...");
        RpcResponse response = new RpcResponse();
        try {
            //根据request来处理具体业务调用
            Object result = handle(request);
            response.setResult(result);
        } catch (Throwable t) {
            response.setError(t);
        }
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    //根据request来处理具体的业务调用
    private Object handle(RpcRequest request) throws Throwable {
        LOGGER.info("开始处理业务...");
        //查找服务类对象
        String className = request.getClassName();
        Object serviceBean = handlerMap.get(className);
        //获取接口
        Class<?> forName = Class.forName(className);
        //拿到要调用的方法名，参数类型，参数值
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Method method = forName.getMethod(methodName, parameterTypes);

        //调用实现类对象的制定方法并返回结果
        Object[] parameters = request.getParameters();
        return method.invoke(serviceBean, parameters);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("server caught exception ", cause);
        ctx.close();
    }
}
